
from copy import copy
from typing import Union

from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *


class MainWindow(QWidget):
    def __init__(self) -> None:
        super().__init__()

        self.setAcceptDrops(True)
        self.resize(960, 540)
        self.setWindowTitle('PictureTable')

        self._images = []

        self._img_now = None
        
        self._mouse_pressed = False
        self._rel_mouse_pos_in_image = None

    def _close_image_callback(self, label: QLabel):
        label.close()
        self._images.remove(label)

    def _get_qlabel_menu_func(self, label):
        def _show_qlabel_menu():
            menu = QMenu(label)
            action = QAction('关闭', menu)
            action.triggered.connect(lambda *_: self._close_image_callback(label))
            menu.addAction(action)
            menu.exec_(QCursor.pos())
        return _show_qlabel_menu

    def dragEnterEvent(self, event: QDragEnterEvent) -> None:
        event.acceptProposedAction()

    def put_image(self, img: Union[QImage, QMovie], adapt_and_center: bool = False) -> QLabel:
        self._img_now = img
        
        label = QLabel(self)
        self._images.insert(0, label)

        if isinstance(img, QMovie):
            label.setMovie(img)
            img.start()
        else:    
            pixmap = QPixmap.fromImage(img)
            label.setPixmap(pixmap)
            label.resize(*self.get_normal_size(img))
        
        label.setScaledContents(True)

        _shadow_effect = QGraphicsDropShadowEffect()
        _shadow_effect.setBlurRadius(20)
        _shadow_effect.setColor(QColor(0, 0, 0, 90))
        _shadow_effect.setOffset(0.0, 0.0)
        label.setGraphicsEffect(_shadow_effect)

        label.show()

        label.setContextMenuPolicy(Qt.CustomContextMenu)
        label.customContextMenuRequested.connect(self._get_qlabel_menu_func(label))

        print(self._images)

        return label

    def _get_image_or_movie(self, path: str) -> Union[QLabel, QMovie]:
        if path[-4:].lower() == '.gif':
            return QMovie(path)
        img = QImage()
        img.load(path)
        return img

    def open_image(self, path) -> QLabel:
        pict = self._get_image_or_movie(path)
        return self.put_image(pict)

    def get_normal_size(self, img: QLabel) -> tuple:
        if img.height() < self.height() and img.width() < self.width():
            return img.width(), img.height()

        side = 1  if img.height() > img.width() else 0

        scale = img.width() / img.height()

        h, w = 0, 0
        
        if side == 1:
            h = self.height()
            w = int(h * scale)
        else:
            w = self.width()
            h = int(w / scale)

        return int(w * 0.8), int(h * 0.8)

    def dropEvent(self, event: QDropEvent) -> None:
        file_urls = event.mimeData().urls()
        if not file_urls:
            return

        url = file_urls[0].toLocalFile()
        print(url)

        pict = self._get_image_or_movie(url)

        img_label = self.put_image(pict)
        img_label.move(
            self.mapFromGlobal(QCursor.pos()) - 
                QPoint(img_label.width() // 2, img_label.height() // 2))

    def _check_mouse_in_image(self, mouse_rel_pos: QPoint, img_label: QLabel):
        x_range = range(img_label.x(), img_label.x() + img_label.width())
        y_range = range(img_label.y(), img_label.y() + img_label.height())

        return mouse_rel_pos.x() in x_range and mouse_rel_pos.y() in y_range

    def mousePressEvent(self, event: QMouseEvent):
        print(self.mapFromGlobal(QCursor.pos()))

        if not self._images:
            return

        for img in self._images:
            if self._check_mouse_in_image(event.pos(), img):
                img.raise_()
                self._images.remove(img)
                self._images.insert(0, img)  # put top
                break
        else:
            return
            
        self._img_now = img
        self._rel_mouse_pos_in_image = img.mapFromParent(event.pos())
        self._mouse_pressed = True

    def mouseMoveEvent(self, event: QMouseEvent):
        if self._mouse_pressed:
            self._images[0].move(event.pos() - self._rel_mouse_pos_in_image)
            self._images[0].raise_()
    
    def mouseReleaseEvent(self, event: QMouseEvent):
        self._mouse_pressed = False

    def wheelEvent(self, event: QWheelEvent):
        if not self._images:
            return

        for img in self._images:
            if self._check_mouse_in_image(event.pos(), img):
                break
        else:
            return

        img: QLabel
        
        mouse_rel_pos = img.mapFromGlobal(QCursor.pos())
        print(mouse_rel_pos)
        xs = mouse_rel_pos.x() / img.width()
        ys = mouse_rel_pos.y() / img.height()

        if event.angleDelta().y() > 0:
            img.resize(img.size() * 1.05)
            mouse_rel_pos_now = img.mapFromGlobal(QCursor.pos())
            move_x = img.width() * xs - mouse_rel_pos_now.x()
            move_y = img.height() * ys - mouse_rel_pos_now.y()
            img.move(img.pos() - QPoint(move_x, move_y))
        else:
            img.resize(img.size() * 0.95)
            mouse_rel_pos_now = img.mapFromGlobal(QCursor.pos())
            move_x = img.width() * xs - mouse_rel_pos_now.x()
            move_y = img.height() * ys - mouse_rel_pos_now.y()
            img.move(img.pos() - QPoint(move_x, move_y))

